grammar ClacGrammar;

options {
  language = Java;
}

@parser::header {
  package com.chimpansoft.claculator.mathParser;

  import java.util.HashMap;
  import java.util.List;
  import java.util.ArrayList;

  import org.apfloat.Apfloat;
  import org.apfloat.ApfloatMath;

}

@lexer::header {
  package com.chimpansoft.claculator.mathParser;
}
 
@members {
  HashMap<String, Apfloat> memory = new HashMap<String, Apfloat>();
  List<Term> statList = new ArrayList<Term>();
  int precision = 13;
  
  public void setMemory(HashMap<String, Apfloat> memory) {
    this.memory = memory;
  }
  
  public void setStatList(List<Term> statList) {
    this.statList = statList;
  }
  
  public void setPrecision(int precision) {
    this.precision = precision;
  }
}

prog returns [List<Term> value]
      : ( s=stat {
           statList.add(new Term($s.text, $s.value));
          }
        )+ {
          $value = statList;
        }
      ;  

/**
 * "Statement":
 * -> entweder Expression
 * -> oder ID = Expression
 */ 
stat returns [Apfloat value]  
      : expr ';' {
          $value = $expr.value;
        }
      | ID '=' expr ';' {
          memory.put($ID.text, $expr.value);
          $value = $expr.value;
        }
      ;
 
/**
 * Additiver Ausdruck
 */ 
expr returns [Apfloat value]  
      : e=multExpr {$value = $e.value;} 
        (   '+' e=multExpr {$value = $value.add($e.value);}
          | '-' e=multExpr {$value = $value.subtract($e.value);}
        )*
      ;

/**
 * Multiplikativer Ausdruck
 */   
multExpr returns [Apfloat value]
      : e1=atom {$value = $e1.value;} 
        (   '*' e2=atom {
              $value = $value.multiply($e2.value);
            }
          | '/' e2=atom {
              $value = $value.divide($e2.value);
            }
          | '^' e2=atom {
              $value = ApfloatMath.pow($value, $e2.value);
          }
        )*
      ;

/**
 * Atomare Einheit
 */ 
atom returns [Apfloat value]
      : INT {
          $value = new Apfloat($INT.text, precision);
        }
      | FLOAT {
          $value = new Apfloat($FLOAT.text, precision);
        }
      | ID {
          Apfloat v = (Apfloat) memory.get($ID.text);
          
          if(v != null)
            $value = v;
          else
            System.err.println("Unknown ID: " + $ID.text);
          
        }
      | '(' expr ')' {
          $value = $expr.value;
        }
      | function {
          $value = $function.value;
        }
      ;
      
function returns [Apfloat value]
      : 'cos(' expr ')' {
          $value = ApfloatMath.cos($expr.value);
        }
      | 'sin(' expr ')' {
          $value = ApfloatMath.sin($expr.value);
        }     
      ;

/**
 * Literale
 */   
ID    : ('a'..'z'|'A'..'Z')+
      ;
  
INT   : '0'..'9'+
      ;

FLOAT : ('0'..'9'+) '.' ('0'..'9'+)
      ;
  
WS    : (' '|'\t'|'\r'|'\n')+ {skip();}
      ;